package cn.virens.web.components.open.wxma.router;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import cn.virens.web.components.open.wxma.WxMaServiceImpl;
import cn.virens.web.components.open.wxma.session.WxMaSessionManagerImpl;
import cn.virens.web.components.open.wxma.session.WxMaSessionMessage;
import cn.virens.web.components.open.wxmp.annotations.WxMpRouterRule;

@Component
public class WxMaRouterImpl implements InitializingBean, DisposableBean, ApplicationContextAware {
	protected final Logger logger = LoggerFactory.getLogger(WxMaRouterImpl.class);

	protected final ExecutorService executor = Executors.newFixedThreadPool(100);
	protected final Map<String, Long> queue = new ConcurrentHashMap<>();
	protected final List<WxMaRouterRuleWarp> rules = new ArrayList<>();

	private @Autowired WxMaSessionManagerImpl wxMaSessionManager;
	private @Autowired WxMaServiceImpl wxMaService;

	private final Long timeout = 15 * 1000L;
	private volatile boolean stop = false;
	private Thread thread;

	public String route(WxMaMessage message) {
		return route(warp(message, new ConcurrentHashMap<>()));
	}

	protected String route(WxMaSessionMessage message) {
		String answer = null;

		if (!isDuplicate(String.valueOf(message.getMsgId()))) {
			for (WxMaRouterRuleWarp rule : matchRules(message)) {
				if (rule != null && !rule.isAsync()) {
					answer = rule.handle(message, wxMaSessionManager);
				} else {
					executor.execute(() -> rule.handle(message, wxMaSessionManager));
				}
			}
		}

		return answer;
	}

	protected WxMaSessionMessage warp(WxMaMessage message, Map<String, Object> attribute) {
		return new WxMaSessionMessage(message, attribute, wxMaService.getAppid());
	}

	protected List<WxMaRouterRuleWarp> matchRules(WxMaSessionMessage message) {
		List<WxMaRouterRuleWarp> rules = new ArrayList<>();

		// 收集匹配的规则
		for (WxMaRouterRuleWarp rule : this.rules) {
			if (rule.test(message) && rules.add(rule)) {
				if (rule.isReEnter()) return rules;
			}
		}

		return rules;
	}

	protected boolean isDuplicate(String messageKey) {
		Long lastTime = this.queue.get(messageKey);
		long now = System.currentTimeMillis();
		if (lastTime == null || (lastTime + timeout) < now) {
			synchronized (queue) {
				this.queue.put(messageKey, now);
				this.queue.notifyAll();
			}
		} else {
			return true;
		}

		return false;
	}

	@Override
	public void destroy() throws Exception {
		synchronized (queue) {
			this.stop = true;
			this.queue.notifyAll();
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		this.thread = new Thread(() -> {
			try {
				while (!stop) {
					// 如果队列为空，就进入等待
					if (this.queue.isEmpty()) {
						synchronized (queue) {
							this.queue.wait();
						}
					}

					// 检查缓存的数据是否过期，如果过期就删除
					long overrTime = System.currentTimeMillis() - timeout;
					for (Entry<String, Long> entry : queue.entrySet()) {
						if (entry.getValue() < overrTime) {
							this.queue.remove(entry.getKey());
						}
					}
				}
			} catch (Exception e) {
				logger.error("清理异常：" + e.getMessage(), e);
			}
		});

		// 启动重复消息检查线程
		this.thread.setName(getClass().getSimpleName());
		this.thread.setDaemon(true);
		this.thread.start();
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		for (WxMaRouterRuleHandler routerHandler : getHandlers(applicationContext).values()) {
			// 获取对应的路由配置信息 & 过滤是否有效
			WxMpRouterRule ruleCfg = getAnnotation(routerHandler);
			if (ruleCfg == null || ruleCfg.ignore()) continue;

			// 构造路由规则包装类
			WxMaRouterRuleWarp routerRule = new WxMaRouterRuleWarp(routerHandler);
			routerRule.setContentRegex(ruleCfg.contentRegex());
			routerRule.setFromUser(ruleCfg.fromUser());
			routerRule.setMsgType(ruleCfg.msgType());
			routerRule.setContent(ruleCfg.content());
			routerRule.setReEnter(ruleCfg.reEnter());
			routerRule.setAsync(ruleCfg.async());
			routerRule.setEvent(ruleCfg.event());
			routerRule.setOrder(ruleCfg.order());

			if (routerRule != null && rules.add(routerRule)) {
				logger.debug("扫描到->" + routerRule.toString());
			}
		}

		// 将所有的路由规则按order进行排序
		Collections.sort(rules, (a, b) -> a.compare(b));
	}

	private final WxMpRouterRule getAnnotation(WxMaRouterRuleHandler routerHandler) {
		return routerHandler.getClass().getAnnotation(WxMpRouterRule.class);
	}

	private final Map<String, WxMaRouterRuleHandler> getHandlers(ApplicationContext applicationContext) {
		return applicationContext.getBeansOfType(WxMaRouterRuleHandler.class);
	}

}
